#ifndef CUBE_MODEL_H_
#define CUBE_MODEL_H_

extern "C" 
{
	#include "lua.h"
	#include "lualib.h"
	#include "lauxlib.h"
};
#include "lua_tinker.h"
#include "actormanager.h"

class	Mesh;

class Actor : public Fundamental::Object
{
DECLARE_RTTI( Actor, Fundamental::Object )
public :
	virtual ~Actor();

	void					Initialize();
	bool					IsRenderable(){ return _isRenderable; }
	void					Update( float deltaTime );

	D3DXMATRIX				GetMatObject(){ return _matObject; }
	D3DXVECTOR3				GetPosition();
	const std::string&		GetType(){ return _type; }

	void					SetPosition( const D3DXVECTOR3& pos );

	template< typename R >
	R						Call( const char* name );
	template< typename R, typename A1 >
	R						Call( const char* name, A1 arg1 );
	template< typename R, typename A1, typename A2 >
	R						Call( const char* name, A1 arg1, A2 arg2 );
	template< typename R, typename A1, typename A2, typename A3 >
	R						Call( const char* name, A1 arg1, A2 arg2, A3 arg3 );
	template< typename R >
	R						GetParam( const char* name );
	template< typename T >	
	bool					IsParam( const char* name, T value );
	template< typename V >
	void					SetParam( const char* name, V value );

	static Actor*			Create( const std::string& type, const std::string& name );

protected :
	Actor( const std::string& type, const std::string& name, ActorTypeInfo& desc );

	void					SetRenderable(){ _isRenderable = true; }

	lua_tinker::table*		_pParams;
	ActorTypeInfo&			_desc;

private :
	bool					_isRenderable;
	D3DXMATRIX				_matObject;
	const std::string		_type;
};

template< typename R >
R Actor::Call( const char* name )
{
#ifdef _DEBUG
	ActorTypeInfo::FuncIter it = _desc.funcs.find( name );
	if( it == _desc.funcs.end() )
	{
		assert( false && "No such Function available!" );
	}
	if( ( it->second )[0] != typeid( R ).name() )
	{
		assert( false && "Return type not matched!" );
	}
#endif

	return lua_tinker::call< R >( _desc.L, name, *_pParams );
}

template< typename R, typename A1 >
R Actor::Call( const char* name, A1 arg1 )
{
#ifdef _DEBUG
	ActorTypeInfo::FuncIter it = _desc.funcs.find( name );
	if( it == _desc.funcs.end() )
	{
		assert( false && "No such Function available!" );
	}
	if( ( it->second )[0] != typeid( R ).name() )
	{
		assert( false && "Return type not matched!" );
	}
	if( ( it->second )[1] != typeid( A1 ).name() )
	{
	}
#endif

	return lua_tinker::call< R >( _desc.L, name, *_pParams, arg1 );
}

template< typename R, typename A1, typename A2 >
R Actor::Call( const char* name, A1 arg1, A2 arg2 )
{
	return lua_tinker::call< R >( _desc.L, name, *_pParams, arg1, arg2 );
}

template< typename R, typename A1, typename A2, typename A3 >
R Actor::Call( const char* name, A1 arg1, A2 arg2, A3 arg3 )
{
	return lua_tinker::call< R >( _desc.L, name, *_pParams, arg1, arg2, arg3 );
}

template< typename R >
R Actor::GetParam( const char* name )
{
#ifdef _DEBUG
	R type = {0,};
	if( !IsParam( name, type ) )
	{
		assert( false && "No such parameter available!" );
	}
#endif

	return _pParams->get< R >( name );
}

template< typename T >
bool Actor::IsParam( const char* name, T value )
{
	ActorTypeInfo::ParamIter it = _desc.params.find( name );
	if( it == _desc.params.end() ||
		it->second != typeid( T ).name() )
	{
		return false;
	}

	return true;
}

template< typename V >
void Actor::SetParam( const char* name, V value )
{
#ifdef _DEBUG
	if( !IsParam( name, value ) )
	{
		assert( false && "No such parameter available!" );
	}
#endif

	_pParams->set< V >( name, value ); 
}

#endif
